package com.atguigu.daijia.dispatch.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.Result;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.dispatch.mapper.OrderJobMapper;
import com.atguigu.daijia.dispatch.service.NewOrderService;
import com.atguigu.daijia.dispatch.xxl.client.XxlJobClient;
import com.atguigu.daijia.map.client.LocationFeignClient;
import com.atguigu.daijia.model.entity.dispatch.OrderJob;
import com.atguigu.daijia.model.enums.OrderStatus;
import com.atguigu.daijia.model.form.map.SearchNearByDriverForm;
import com.atguigu.daijia.model.vo.dispatch.NewOrderTaskVo;
import com.atguigu.daijia.model.vo.map.NearByDriverVo;
import com.atguigu.daijia.model.vo.order.NewOrderDataVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author 谢忠涵7
 */
@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class NewOrderServiceImpl implements NewOrderService {

    @Autowired
    private OrderJobMapper orderJobMapper;
    @Autowired
    private XxlJobClient xxlJobClient;
    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;
    @Autowired
    private LocationFeignClient locationFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * @description: 添加并开始新订单任务调度
     * @author: xiaoxie
     * @date: 2024/10/27 16:11
     * @param: [newOrderTaskVo]
     * @return: 返回任务id
     **/
    @Override
    public Long addAndStartTask(NewOrderTaskVo newOrderTaskVo) {
        LambdaQueryWrapper<OrderJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderJob::getOrderId, newOrderTaskVo.getOrderId());
        OrderJob orderJob = orderJobMapper.selectOne(wrapper);
        if (orderJob == null) {
         String core = "0 0/1 * * * ?";
         String desc = "新订单任务,订单id"+newOrderTaskVo.getOrderId();
            Long jobId = xxlJobClient.addAndStart("newOrderTaskHandler", "",
                    core, desc);
            orderJob = new OrderJob();
         orderJob.setOrderId(newOrderTaskVo.getOrderId());
         orderJob.setJobId(jobId);
         orderJob.setParameter(JSONObject.toJSONString(newOrderTaskVo));
         orderJobMapper.insert(orderJob);
        }
        return orderJob.getJobId();
    }

    /**
     * @description: 执行任务的具体内容
     * @author: xiaoxie
     * @date: 2024/10/27 19:31
     * @param: [jobId] 任务id
     * @return: java.lang.Long
     **/
    @Override
    public void execute(long jobId) {
        //1.获取订单信息
        LambdaQueryWrapper<OrderJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderJob::getJobId, jobId);
        OrderJob orderJob = orderJobMapper.selectOne(wrapper);
        Result<Integer> orderStatus = orderInfoFeignClient.getOrderStatus(orderJob.getOrderId());
        if(orderStatus.getCode() != 200){
            throw new GuiguException(ResultCodeEnum.FEIGN_FAIL);
        }
        Integer status = orderStatus.getData();
        //2.判断订单状态是否为正在接单
        if(status != 1) {
            //3.如果不在接单状态，则停止定时调度
            xxlJobClient.stopJob(jobId);
        }

        //4.搜索附件满足条件的司机
        SearchNearByDriverForm searchNearByDriverForm = new SearchNearByDriverForm();
        NewOrderTaskVo newOrderTaskVo = JSONObject.parseObject(orderJob.getParameter()
                , NewOrderTaskVo.class);
        searchNearByDriverForm.setLongitude(newOrderTaskVo.getStartPointLongitude());
        searchNearByDriverForm.setLatitude(newOrderTaskVo.getEndPointLatitude());
        searchNearByDriverForm.setMileageDistance(newOrderTaskVo.getExpectDistance());
        Result<List<NearByDriverVo>> listResult = locationFeignClient
                .searchNearByDriver(searchNearByDriverForm);
        if (listResult.getCode() != 200) {
            throw new GuiguException(ResultCodeEnum.FEIGN_FAIL);
        }
        List<NearByDriverVo> resultData = listResult.getData();
        resultData.forEach(nearByDriverVo ->{
            //将订单信息推送给每个司机 set
            String repeatKey = RedisConstant.DRIVER_ORDER_REPEAT_LIST
                    +newOrderTaskVo.getOrderId();
            Boolean isMember = redisTemplate.opsForSet()
                    .isMember(repeatKey, nearByDriverVo.getDriverId());
            if(!isMember){
                //记录该订单已放入司机临时容器
                redisTemplate.opsForSet().add(repeatKey, nearByDriverVo.getDriverId());
                //过期时间：15分钟，新订单15分钟没人接单自动取消
                redisTemplate.expire(repeatKey,
                        RedisConstant.DRIVER_ORDER_REPEAT_LIST_EXPIRES_TIME
                        , TimeUnit.MINUTES);
            }
            NewOrderDataVo newOrderDataVo = new NewOrderDataVo();
            BeanUtils.copyProperties(newOrderTaskVo, newOrderDataVo);
            newOrderDataVo.setDistance(nearByDriverVo.getDistance());
            //每个订单的有效期为1分钟,司机如果没有接单就从订单列表中删除
            //新订单保存司机的临时队列，Redis里面List集合
            //将消息保存到司机的临时队列里面，司机接单了会定时轮询到他的临时队列获取订单消息
            String key = RedisConstant.DRIVER_ORDER_TEMP_LIST
                    +nearByDriverVo.getDriverId();
            redisTemplate.opsForList()
                    .leftPush(key, JSONObject.toJSONString(newOrderDataVo));
            //过期时间：1分钟，1分钟未消费，自动过期
            //注：司机端开启接单，前端每5秒（远小于1分钟）拉取1次“司机临时队列”里面的新订单消息
            redisTemplate.expire(key,
                    RedisConstant.DRIVER_ORDER_TEMP_LIST_EXPIRES_TIME
                    , TimeUnit.MINUTES);
            log.info("该新订单信息已放入司机临时队列: {}"
                    , JSON.toJSONString(newOrderDataVo));
        });

    }

    /**
     * @description: 查询司机新订单数据
     * @author: xiaoxie
     * @date: 2024/10/27 20:18
     * @param: [driverId] 司机id
     * @return: 订单数据
     **/

    @Override
    public List<NewOrderDataVo> findNewOrderQueueData(Long driverId) {
        List<NewOrderDataVo> list = new ArrayList<>();
        String key = RedisConstant.DRIVER_ORDER_TEMP_LIST + driverId;
        Long size = redisTemplate.opsForList().size(key);
        if( size == null || size == 0){
            return null;
        }
        for(int i=0; i<size; i++) {
            String content = (String)redisTemplate.opsForList().leftPop(key);
            NewOrderDataVo newOrderDataVo = JSONObject.parseObject(content, NewOrderDataVo.class);
            list.add(newOrderDataVo);
        }
        return list;
    }
    /**
     * @description: 清空新订单队列数据
     * @author: xiaoxie
     * @date: 2024/10/27 20:19
     * @param: [driverId] 司机id
     * @return: java.lang.Boolean
     **/
    @Override
    public Boolean clearNewOrderQueueData(Long driverId) {
        String key = RedisConstant.DRIVER_ORDER_TEMP_LIST + driverId;
        //直接删除，司机开启服务后，有新订单会自动创建容器
        redisTemplate.delete(key);
        return true;
    }
}
